package cn.seqdata.cxf.oauth2;

import java.util.Collections;
import java.util.List;

import org.apache.cxf.rs.security.oauth2.common.Client;
import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken;
import org.apache.cxf.rs.security.oauth2.common.UserSubject;
import org.apache.cxf.rs.security.oauth2.provider.AbstractOAuthDataProvider;
import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
import org.apache.cxf.rs.security.oauth2.tokens.refresh.RefreshToken;
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;

public class SpringCacheOAuthDataProvider extends AbstractOAuthDataProvider {
	public final Cache clientCache;
	public final Cache accessTokenCache;
	public final Cache refreshTokenCache;
	private final OAuthStore store;

	public SpringCacheOAuthDataProvider(CacheManager cacheManager, OAuthStore store) {
		this.store = store;

		clientCache = cacheManager.getCache(OAuthConstants.CLIENT_ID);
		accessTokenCache = cacheManager.getCache(OAuthConstants.ACCESS_TOKEN);
		refreshTokenCache = cacheManager.getCache(OAuthConstants.REFRESH_TOKEN);
	}

	@Override
	protected Client doGetClient(String clientId) {
		Client client = clientCache.get(clientId, Client.class);

		if (null == client) {
			client = store.selectClient(clientId);
			setClient(client);
		}

		return client;
	}

	@Override
	public List<Client> getClients(UserSubject resourceOwner) {
		Client client = getClient(resourceOwner.getLogin());
		return null != client ? Collections.singletonList(client) : Collections.emptyList();
	}

	@Override
	public void setClient(Client client) {
		if (null != client)
			clientCache.put(client.getClientId(), client);
	}

	@Override
	protected void doRemoveClient(Client c) {
		clientCache.evict(c.getClientId());
	}

	@Override
	public ServerAccessToken getAccessToken(String tokenKey) throws OAuthServiceException {
		ServerAccessToken token = accessTokenCache.get(tokenKey, ServerAccessToken.class);

		if (null == token) {
			token = store.selectAccessToken(tokenKey);
		}

		if (null != token) {
			Client client = token.getClient();
			String clientId = client.getClientId();

			// 用缓存的client换掉token的client
			token.setClient(getClient(clientId));
			accessTokenCache.put(tokenKey, token);

			// 如果token过期，删除并且返回null
			if (OAuthUtils.isExpired(token.getIssuedAt(), token.getExpiresIn())) {
				accessTokenCache.evict(tokenKey);
				store.deleteAccessToken(tokenKey);
				token = null;
			}
		}

		return token;
	}

	@Override
	public List<ServerAccessToken> getAccessTokens(Client client, UserSubject subject) throws OAuthServiceException {
		List<ServerAccessToken> tokens = store.selectClientAccessTokens(client.getClientId());

		if (null != tokens)
			for (ServerAccessToken token : tokens)
				token.setClient(client);

		return tokens;
	}

	@Override
	protected final void saveAccessToken(ServerAccessToken token) {
		accessTokenCache.put(token.getTokenKey(), token);
		store.updateAccessToken(token);
	}

	@Override
	protected void doRevokeAccessToken(ServerAccessToken accessToken) {
		accessTokenCache.evict(accessToken.getTokenKey());
		store.deleteAccessToken(accessToken.getTokenKey());
	}

	@Override
	protected RefreshToken getRefreshToken(String tokenKey) {
		RefreshToken token = refreshTokenCache.get(tokenKey, RefreshToken.class);

		if (null == token) {
			token = store.selectRefreshToken(tokenKey);
			if (null != token) {
				// 用缓存的client换掉token的client
				token.setClient(getClient(token.getClient()
					.getClientId()));
				refreshTokenCache.put(tokenKey, token);
			}
		}

		if (token != null && OAuthUtils.isExpired(token.getIssuedAt(), token.getExpiresIn())) {
			refreshTokenCache.evict(tokenKey);
			store.deleteRefreshToken(tokenKey);
			token = null;
		}

		return token;
	}

	@Override
	public List<RefreshToken> getRefreshTokens(Client client, UserSubject subject) throws OAuthServiceException {
		List<RefreshToken> tokens = store.selectClientRefreshTokens(client.getClientId());

		if (null != tokens)
			for (RefreshToken token : tokens)
				token.setClient(client);

		return tokens;
	}

	@Override
	protected final void saveRefreshToken(RefreshToken token) {
		refreshTokenCache.put(token.getTokenKey(), token);
		store.updateRefreshToken(token);
	}

	@Override
	protected void doRevokeRefreshToken(RefreshToken token) {
		refreshTokenCache.evict(token.getTokenKey());
		store.deleteRefreshToken(token.getTokenKey());
	}
}
